using System.Net.Sockets;
using Gavel.Common.Component.Helper;
using Gavel.Common.Utilities.Const;
using Gavel.Common.Utilities.Model;
using ProtocolType = System.Net.Sockets.ProtocolType;

namespace Gavel.ModLink.Helper;

/// <summary>
/// 客户端帮助类
/// </summary>
public class ClientHelper
{
    #region 单例构造

    /// <summary>
    /// 单例锁
    /// </summary>
    private static readonly object Padlock = new();

    /// <summary>
    /// 单例对象
    /// </summary>
    private static ClientHelper? _instance;

    /// <summary>
    /// 私有化构造
    /// </summary>
    private ClientHelper()
    {
    }

    /// <summary>
    /// 单例对象
    /// </summary>
    public static ClientHelper Instance
    {
        get
        {
            lock (Padlock)
            {
                return _instance ??= new ClientHelper();
            }
        }
    }

    #endregion 单例构造

    #region 属性字段

    /// <summary>
    /// 套接字
    /// </summary>
    private Socket? _socket;

    /// <summary>
    /// 报文发送信号量
    /// </summary>
    private readonly SemaphoreSlim _sendLock = new(1);

    /// <summary>
    /// 套接字状态改变处理
    /// </summary>
    public delegate void SocketStatusChangedHandler(SocketStatus status);

    /// <summary>
    /// 套接字状态改变事件
    /// </summary>
    public event SocketStatusChangedHandler? SocketStatusChangedEvent;

    /// <summary>
    /// 套接字接收到数据处理
    /// </summary>
    public delegate void ReceivedHandler(MessageTransmit transmit);

    /// <summary>
    /// 套接字接收到数据事件
    /// </summary>
    public event ReceivedHandler? ReceivedEvent;

    /// <summary>
    /// 报文发送成功处理
    /// </summary>
    public delegate void SentHandler(MessageTransmit transmit);

    /// <summary>
    /// 报文发送成功事件
    /// </summary>
    public event SentHandler? SentEvent;

    /// <summary>
    /// 判断套接字是否已连接
    /// </summary>
    public bool Connected
    {
        get
        {
            if (_socket == null)
            {
                return false;
            }

            try
            {
                // 使用Poll进行连接状态检测
                bool isAlive = !(_socket.Poll(1, SelectMode.SelectRead) && _socket.Available == 0);
                return _socket.Connected && isAlive;
            }
            catch
            {
                return false;
            }
        }
    }

    #endregion 属性字段

    #region 业务方法

    /// <summary>
    /// 套接字连接
    /// </summary>
    /// <param name="connection">网络连接参数</param>
    public async Task Connect(Connection connection)
    {
        try
        {
            // 当套接字不为空，则需要先将其回收
            await Disconnect();
            // 初始化套接字：1、支持IPV4 2、支持可靠的、双向的、基于连接的字节流，不重复数据，也不保留边界 3、TCP传输控制协议
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            _socket.ReceiveTimeout = 1000;
            Task connectAsync = _socket.ConnectAsync(connection.Ip, connection.Port);
            SocketStatusChangedEvent?.Invoke(SocketStatus.Connecting);
            await connectAsync;
        }
        catch (Exception e)
        {
            NotifyHelper.Instance.Error($"客户端连接异常\n{e.Message}");
        }
        finally
        {
            SocketStatusChangedEvent?.Invoke(_socket is not null && _socket.Connected && Connected ? SocketStatus.Connected : SocketStatus.Normal);
        }
    }

    /// <summary>
    /// 断开连接
    /// </summary>
    public async Task Disconnect()
    {
        try
        {
            if (_socket is null)
            {
                return;
            }

            await _socket.DisconnectAsync(false);
            _socket.Shutdown(SocketShutdown.Both);
            _socket.Dispose();
            _socket = null;
        }
        catch (Exception e)
        {
            NotifyHelper.Instance.Error($"客户端断开连接异常\n{e.Message}");
        }
        finally
        {
            SocketStatusChangedEvent?.Invoke(_socket is not null && _socket.Connected && Connected ? SocketStatus.Connected : SocketStatus.Normal);
        }
    }

    /// <summary>
    /// 发送数据
    /// </summary>
    /// <param name="transmit">报文传输</param>
    public async Task Send(MessageTransmit transmit)
    {
        try
        {
            if (Connected == false)
            {
                return;
            }

            // 使用信号量限制一次只能发送一条
            await _sendLock.WaitAsync();
            SentEvent?.Invoke(transmit);
            await _socket!.SendAsync(new ArraySegment<byte>(transmit.Data), SocketFlags.None);
            await Receive(transmit);
        }
        catch (Exception e)
        {
            NotifyHelper.Instance.Error($"客户端发送数据异常\n{e.Message}");
        }
        finally
        {
            _sendLock.Release();
            SocketStatusChangedEvent?.Invoke(_socket is not null && _socket.Connected && Connected ? SocketStatus.Connected : SocketStatus.Normal);
        }
    }

    /// <summary>
    /// 接收数据
    /// </summary>
    /// <param name="transmit">报文传输</param>
    private async Task Receive(MessageTransmit transmit)
    {
        try
        {
            if (_socket is null || Connected == false)
            {
                return;
            }

            byte[] buffer = new byte[ushort.MaxValue];
            int seek;
            using (CancellationTokenSource cts = new CancellationTokenSource())
            {
                cts.CancelAfter(_socket.ReceiveTimeout);
                seek = await _socket.ReceiveAsync(buffer, SocketFlags.None, cts.Token);
            }

            if (seek == 0)
            {
                return;
            }

            byte[] received = new byte[seek];
            Array.Copy(buffer, received, seek);
            MessageTransmit message = new MessageTransmit(transmit.Protocol, transmit.Function, TransmitWay.Receive, transmit.Address, received);
            ReceivedEvent?.Invoke(message);
        }
        catch (OperationCanceledException)
        {
            NotifyHelper.Instance.Error("等待回复超时\n请检查协议或寄存器配置");
        }
        catch (SocketException e)
        {
            NotifyHelper.Instance.Error(e.SocketErrorCode == SocketError.TimedOut ? "等待回复超时\n请检查协议或寄存器配置" : e.Message);
        }
        catch (Exception e)
        {
            NotifyHelper.Instance.Error($"{e.Message}\n请检查各项配置是否正确。");
        }
        finally
        {
            SocketStatusChangedEvent?.Invoke(_socket is not null && _socket.Connected && Connected ? SocketStatus.Connected : SocketStatus.Normal);
        }
    }

    #endregion 业务方法
}